package com.agileai.weixin.core;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.agileai.weixin.model.Constans;
import com.agileai.weixin.model.Constans.EventType;
import com.agileai.weixin.model.Constans.ReqType;
import com.agileai.weixin.tool.MessageBuilder;

public class MessageBrokerServlet extends HttpServlet{
	private static final long serialVersionUID = 4440739483644821986L;
	private static String MessageEventHandlerClassName = null;
	protected static Logger log = Logger.getLogger(MessageBrokerServlet.class);
	
	@Override
	public void init() throws ServletException {
		Constans.Configs.APPID = this.getInitParameter("APPID");
		Constans.Configs.APPSECRET = this.getInitParameter("APPSECRET");
		Constans.Configs.TOKEN = this.getInitParameter("TOKEN");
		Constans.Configs.BAIDU_KEY = this.getInitParameter("BAIDU_KEY");
		
		String locaionCacheMinutes = this.getInitParameter("LOCAION_CACHE_MINUTES");
		if (locaionCacheMinutes != null && !locaionCacheMinutes.trim().equals("")){
			Constans.Configs.LOCAION_CACHE_MINUTES = locaionCacheMinutes;
		}
		
		String accessTokenCacheMinutes = this.getInitParameter("ACCESSTOKEN_CACHE_MINUTES");
		if (accessTokenCacheMinutes != null && !accessTokenCacheMinutes.trim().equals("")){
			Constans.Configs.ACCESSTOKEN_CACHE_MINUTES = accessTokenCacheMinutes;
		}
		
		MessageEventHandlerClassName = this.getInitParameter("MessageEventHandlerClassName");
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		String echostr = request.getParameter("echostr");
		PrintWriter out = response.getWriter();
		if (checkSignature(signature, timestamp, nonce)) {
			out.print(echostr);
		}
		out.close();
		out = null;
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");

		String respMessage = processRequest(request,response);
		
		PrintWriter out = response.getWriter();
		out.print(respMessage);
		
		out.close();
	}

	private String processRequest(HttpServletRequest request,HttpServletResponse response) {
		String respMessage = null;
		Map<String, String> requestMap = null;
		MessageEventHandler messageEventHandler = null;
		try {
			requestMap = MessageBuilder.parseXml(request);	
			Class<?> handlerClass = Thread.currentThread().getContextClassLoader().loadClass(MessageEventHandlerClassName);
			messageEventHandler = (MessageEventHandler)handlerClass.newInstance();
			messageEventHandler.setContextProperties(this, request, response);
		} catch (Exception e) {
			e.printStackTrace();
			log.error(e.getLocalizedMessage(), e);
			return "";
		}
//		log.debug("requestMap is " + requestMap);
		System.out.println("requestMap is " + requestMap);
		String msgType = requestMap.get("MsgType");
		if (msgType.equals(ReqType.EVENT)) {
			String eventType = requestMap.get("Event");
			String ticket = requestMap.get("Ticket");
			if (ticket != null) {
				respMessage = messageEventHandler.handleQrCodeEvent(requestMap);
			}
			if (eventType.equals(EventType.SUBSCRIBE)) {
				respMessage = messageEventHandler.handleSubscribe(requestMap);
			}
			else if (eventType.equals(EventType.UNSUBSCRIBE)) {
				respMessage = messageEventHandler.handleUnsubscribe(requestMap);
			}
			else if (eventType.equals(EventType.CLICK)) {
				String eventKey = requestMap.get("EventKey");
				respMessage = messageEventHandler.handleMenuClickEvent(eventKey, requestMap);
			}
			else if (eventType.equals(EventType.VIEW)) {
				String eventKey = requestMap.get("EventKey");
				respMessage = messageEventHandler.handleMenuViewEvent(eventKey, requestMap);
			}
			else if (eventType.equals(EventType.LOCATION)) {
				respMessage = messageEventHandler.handleLocationEvent(requestMap);
			}
		} else {
			if (msgType.equals(ReqType.TEXT)) {
				respMessage = messageEventHandler.handleTextMessage(requestMap);
			}
			else if (msgType.equals(ReqType.IMAGE)) {
				respMessage = messageEventHandler.handleImageMessage(requestMap);
			}
			else if (msgType.equals(ReqType.VOICE)) {
				respMessage = messageEventHandler.handleVoiceMessage(requestMap);
			}
			else if (msgType.equals(ReqType.VIDEO)) {
				respMessage = messageEventHandler.handleVideoMessage(requestMap);
			}
			else if (msgType.equals(ReqType.LOCATION)) {
				respMessage = messageEventHandler.handleLocationMessage(requestMap);
			}
			else if (msgType.equals(ReqType.LINK)) {
				respMessage = messageEventHandler.handleLinkMessage(requestMap);
			}
		}
		if (respMessage == null) {
			return "";
		}
		return respMessage;
	}
	
	private boolean checkSignature(String signature, String timestamp, String nonce) {
		String[] arr = new String[] {Constans.Configs.TOKEN, timestamp, nonce };
		Arrays.sort(arr);
		StringBuilder content = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			content.append(arr[i]);
		}
		MessageDigest md = null;
		String tmpStr = null;
		try {
			md = MessageDigest.getInstance("SHA-1");
			byte[] digest = md.digest(content.toString().getBytes());
			tmpStr = byteToStr(digest);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		content = null;
		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
	}
	private String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
			strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}
	private String byteToHexStr(byte mByte) {
		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		char[] tempArr = new char[2];
		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
		tempArr[1] = Digit[mByte & 0X0F];

		String s = new String(tempArr);
		return s;
	}
}
